function Tictactoe(canvasElementId, containerElementId, params) {
    if (typeof params === 'undefined') {
        params = {};
    }
    this.size   = params.size || 51;
    this.one    = params.one || {w: 32, h: 32};
    this.value  = params.value || 1; // 1 - x , 2 - o
    
    this.game = new Array();
    for (var x = 0; x < this.size; x++) {
        this.game[x] = new Array();
        for (var y = 0; y < this.size; y++) {
            this.game[x][y] = 0;
        }
    }
    
    this.canvas     = document.getElementById(canvasElementId);
    this.container  = document.getElementById(containerElementId);
    this.canvasWidth = this.canvas.width = this.container.clientWidth;
    this.canvasHeight = this.canvas.height = this.container.clientHeight;

    this.touchEvents = false;
}

Tictactoe.prototype.init = function() {
    var s = new CanvasState(this);
}

/**
 * Ustawia obiekt gry
 * 
 * @param {Array} game
 */
Tictactoe.prototype.setGame = function(game) {
    this.game = game;
}

/**
 * Zwraca obiekt gry
 * 
 * @returns {Array}
 */
Tictactoe.prototype.getGame = function() {
    return this.game;
}

/**
 * Ustawia wartosc w obiekcie gry
 * 
 * @param int x
 * @param int y
 * @returns int
 */
Tictactoe.prototype.setValue = function(x, y) {
    this.game[x][y] = this.value;
}

/**
 * Zwraca wartosc w obiekcie gry
 * 
 * @param int x
 * @param int y
 * @returns int
 */
Tictactoe.prototype.getValue = function(x, y) {
    return this.game[x][y];
}



function CanvasState(tictactoe) {
    // **** First some setup! ****
    
    this.tictactoe = tictactoe;
    
    this.touchEvents = tictactoe.touchEvents;
    this.one = tictactoe.one;
    this.canvas = tictactoe.canvas;
    this.width = this.canvas.width;
    this.height = this.canvas.height;
    this.ctx = this.canvas.getContext('2d');
    this.shape = new Shape(tictactoe);

    // This complicates things a little but but fixes mouse co-ordinate problems
    // when there's a border or padding. See getMouse for more detail
    var stylePaddingLeft, stylePaddingTop, styleBorderLeft, styleBorderTop;
    if (document.defaultView && document.defaultView.getComputedStyle) {
        this.stylePaddingLeft = parseInt(document.defaultView.getComputedStyle(this.canvas, null)['paddingLeft'], 10) || 0;
        this.stylePaddingTop = parseInt(document.defaultView.getComputedStyle(this.canvas, null)['paddingTop'], 10) || 0;
        this.styleBorderLeft = parseInt(document.defaultView.getComputedStyle(this.canvas, null)['borderLeftWidth'], 10) || 0;
        this.styleBorderTop = parseInt(document.defaultView.getComputedStyle(this.canvas, null)['borderTopWidth'], 10) || 0;
    }
    // Some pages have fixed-position bars (like the stumbleupon bar) at the top or left of the page
    // They will mess up mouse coordinates and this fixes that
    var html = document.body.parentNode;
    this.htmlTop = html.offsetTop;
    this.htmlLeft = html.offsetLeft;

    // **** Keep track of state! ****

    this.valid = false; // when set to false, the canvas will redraw everything
    this.dragging = false; // Keep track of when we are dragging
    // the current selected object. In the future we could turn this into an array for multiple selection
    this.selection = null;
    this.dragoffx = 0; // See mousedown and mousemove events for explanation
    this.dragoffy = 0;

    // **** Then events! ****

    // This is an example of a closure!
    // Right here "this" means the CanvasState. But we are making events on the Canvas itself,
    // and when the events are fired on the canvas the variable "this" is going to mean the canvas!
    // Since we still want to use this particular CanvasState in the events we have to save a reference to it.
    // This is our reference!
    var myState = this;

    //fixes a problem where double clicking causes text to get selected on the canvas
    this.canvas.addEventListener('selectstart', function(e) {
        e.preventDefault();
        return false;
    }, false);

    function check(mouseX, mouseY) {
        mouseX -= myState.shape.x;
        mouseY -= myState.shape.y;

        var x = Math.floor(mouseX / myState.one.w);
        var y = Math.floor(mouseY / myState.one.h);

        myState.tictactoe.setValue(x,y);
        myState.valid = false;
        myState.draw();
    }

    // Up, down, and move are for dragging
    if (this.touchEvents) {
        this.canvas.addEventListener('touchstart', function(e) {
            console.log('touchstart');
            touchdown(e);
        }, true);
    } else {
        this.canvas.addEventListener('mousedown', function(e) {
            console.log('mousedown');
            touchdown(e);
        }, true);
    }

    function touchdown(e) {
        var mouse = myState.getMouse(e);
        var mx = mouse.x;
        var my = mouse.y;

        // Keep track of where in the object we clicked
        // so we can move it smoothly (see mousemove)
        myState.dragoffx = mx - myState.shape.x;
        myState.dragoffy = my - myState.shape.y;
        myState.dragging = true;
        myState.valid = false;


        // detecting if after 200ms the finger is still in the same position
        var x = myState.shape.x;
        var y = myState.shape.y;
        setTimeout(function() {
            if ((myState.shape.x === x) && !myState.dragging && (myState.shape.y === y)) {
                // Here you get the Tap event
                check(mx, my);
            }
        }, 200);
    }

    if (this.touchEvents) {
        this.canvas.addEventListener('touchmove', function(e) {
            console.log('touchmove');
            touchmove(e);
        }, true);
    } else {
        this.canvas.addEventListener('mousemove', function(e) {
            console.log('mousemove');
            touchmove(e);
        }, true);
    }


    function touchmove(e) {
        if (myState.dragging) {
            var mouse = myState.getMouse(e);

            // We don't want to drag the object by its top-left corner, we want to drag it
            // from where we clicked. Thats why we saved the offset and use it here
            myState.shape.x = mouse.x - myState.dragoffx;
            myState.shape.y = mouse.y - myState.dragoffy;

            myState.valid = false; // Something's dragging so we must redraw
        }
    }

    if (this.touchEvents) {
        this.canvas.addEventListener('touchend', function(e) {
            console.log('touchend');
            touchup(e);
        }, true);
        this.canvas.addEventListener('touchleave', function(e) {
            console.log('touchleave');
            touchup(e);
        }, true);
    } else {
        this.canvas.addEventListener('mouseup', function(e) {
            console.log('mouseup');
            touchup(e);
        }, true);
        this.canvas.addEventListener('mouseout', function(e) {
            console.log('mouseout');
            touchup(e);
        }, true);
    }


    function touchup(e) {
        myState.dragging = false;
    }

    // **** Options! ****

    this.selectionColor = '#CC0000';
    this.selectionWidth = 2;
    this.interval = 30;
    setInterval(function() {
        myState.draw();
    }, myState.interval);
}

CanvasState.prototype.clear = function() {
    this.ctx.clearRect(0, 0, this.width, this.height);
}

// While draw is called as often as the INTERVAL variable demands,
// It only ever does something if the canvas gets invalidated by our code
CanvasState.prototype.draw = function() {
    // if our state is invalid, redraw and validate!
    if (!this.valid) {
        var ctx = this.ctx;

        this.clear();

        this.shape.draw(ctx);

        this.valid = true;
    }
}

// Creates an object with x and y defined, set to the mouse position relative to the state's canvas
// If you wanna be super-correct this can be tricky, we have to worry about padding and borders
CanvasState.prototype.getMouse = function(e) {

    var element = this.canvas, offsetX = 0, offsetY = 0, mx, my;

    // Compute the total offset
    if (element.offsetParent !== undefined) {
        do {
            offsetX += element.offsetLeft;
            offsetY += element.offsetTop;
        } while ((element = element.offsetParent));
    }

    // Add padding and border style widths to offset
    // Also add the <html> offsets in case there's a position:fixed bar
    offsetX += this.stylePaddingLeft + this.styleBorderLeft + this.htmlLeft;
    offsetY += this.stylePaddingTop + this.styleBorderTop + this.htmlTop;

    // mouse or touch event 
    var eventX = e.pageX || e.touches[0].clientX;
    var eventY = e.pageY || e.touches[0].clientY;

    mx = eventX - offsetX;
    my = eventY - offsetY;

    // We return a simple javascript object (a hash) with x and y defined
    return {x: mx, y: my};
}



function Shape(tictactoe) {
    // This is a very simple and unsafe constructor. All we're doing is checking if the values exist.
    // "x || 0" just means "if there is a value for x, use that. Otherwise use 0."
    // But we aren't checking anything else! We could put "Lalala" for the value of x 
    
    this.tictactoe = tictactoe;
    
    this.x = 0;
    this.y = 0;
    this.size = tictactoe.size;
    this.one = tictactoe.one;
    this.w = this.one.w * this.size;
    this.h = this.one.h * this.size;
    this.maxLeft = tictactoe.canvasWidth - this.w;
    this.maxTop = tictactoe.canvasHeight - this.h;
}

// Draws this shape to a given context
Shape.prototype.draw = function(ctx) {

    // scroll limit x
    if (this.x > 0)
        this.x = 0;
    if (this.x < this.maxLeft)
        this.x = this.maxLeft;

    // scroll limit y
    if (this.y > 0)
        this.y = 0;
    if (this.y < this.maxTop)
        this.y = this.maxTop;

    ctx.beginPath();
    // draw
    var x = this.x;
    for (var x = 0; x < this.w; x = x + this.one.w) {
        pointX = x + this.x;
        ctx.moveTo(pointX, 0);
        ctx.lineTo(pointX, this.h);
    }
    for (var y = 0; y < this.h; y = y + this.one.h) {
        pointY = y + this.y;
        ctx.moveTo(0, pointY);
        ctx.lineTo(this.w, pointY);
    }
    ctx.strokeStyle = "#000000";
    ctx.lineWidth = 1;
    ctx.stroke();
    ctx.closePath();

    ctx.beginPath();
    ctx.strokeStyle = "#FF0000";
    ctx.lineWidth = 2;
    for (var x = 0; x < this.size; x++) {
        for (var y = 0; y < this.size; y++) {
            if (this.tictactoe.getValue(x,y) === 1) {
                ctx.fillRect(x * this.one.w + this.x, y * this.one.h + this.y, this.one.w, this.one.h);
            }
        }
    }
    ctx.closePath();

    ctx.beginPath();
    ctx.strokeStyle = "#FF0000";
    ctx.lineWidth = 4;
    ctx.strokeRect(this.x, this.y, this.w, this.h);
    ctx.closePath();
}
